/* 
 * File:   threads.cpp
 * Author: f10-kafedra
 * 
 * Created on 26 Октябрь 2011 г., 12:27
 */

#include <iostream>
#include "threads.h"
#include "mainForm.h"

thread_LoadFile::thread_LoadFile(QString filename) 
{
    cout << "in thread" << endl;
    this->filecfg = filename;
    boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
    //wndLoadInfos.show();
    //boost::thread thrd(boost::bind(run, boost::ref(this)));
    //boost::thread thrd(this::run);
    //thrd.join();
}

thread_LoadFile::~thread_LoadFile() 
{
    
}

void thread_LoadFile::run()
{
    int count = 0;
    QString line;
    QRegExp re;
    re.setPattern("(\\d):(.*)");
    QFile fileBD(filecfg);
    if (fileBD.open(QIODevice::Text | QIODevice::ReadOnly))
    {
        QTextStream in(&fileBD);
        while (!in.atEnd())
        {
            line = in.readLine();
            if ((count % 1000) == 0)
                cout << count << endl;
            //cout << line.toAscii().data() << endl;
            if (re.indexIn(line) != -1)
            {
                switch (re.cap(1).toInt())
                {
                    case 1:
                    {
                        inform.target = re.cap(2);
                        break;
                    }
                    case 2:
                    {
                        inform.typeInfo = re.cap(2);
                        break;
                    }                
                    case 3:
                    {
                        inform.topic = re.cap(2);
                        break;
                    }
                    case 4:
                    {
                        inform.source = re.cap(2);
                        break;
                    }
                    case 5:
                    {
                        inform.knowledge = re.cap(2);
                        break;
                    }
                    case 0:
                    {
                        infos->push_back(inform);
                        count++;
                        inform.knowledge.clear();
                        inform.source.clear();
                        inform.topic.clear();
                        break;
                    }
                    
                }
            }
            
        }
        cout << "infos size: " << infos->size() << endl;
        fileBD.close();
    }    
    
    cout << "thread over" << endl;
    //wndLoadInfos.hide();
}

/******************************************************************************/

thread_Engine::thread_Engine()
{
    reqCounter = 0;
    login = "f10";
    pass = "f10";
    needReadAnswer = false;
    serverModeOutput = false;
    listen = true;
    AddingRequest = false;
    req = IDLE;
    tcpSocket = new QTcpSocket();
    inWork = false;
    //connect(tcpSocket, SIGNAL(connected()), this, SLOT(slotConnected())); 
    //connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(slotDisConnected()), Qt::DirectConnection); 
    //connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(slotReadBytes()), Qt::DirectConnection);
}

thread_Engine::~thread_Engine()
{
   
    delete tcpSocket;
}

void thread_Engine::run()
{
    mainLoop();
}

void thread_Engine::mainLoop()
{
    cout << "timer!" << endl;
    
    do
    {
/*        if (needReadAnswer)
            tcpSocket->connect(tcpSocket, SIGNAL(readyRead()), tcpSocket, SLOT(slotReadBytes()), Qt::DirectConnection);
        else
            tcpSocket->disconnect(tcpSocket, SIGNAL(readyRead()), tcpSocket, SLOT(slotReadBytes));
**/        
        // обработка очереди
        
        if (AddingRequest == false)
            QueueOperation();        
        
        if (listen)
        {
#ifdef DEBUG_OUTPUT
            if (!serverModeOutput)
            {
                threadMessage = "server in listening mode";
                cout << threadMessage.toAscii().data() << endl;
                
                serverModeOutput = true;
            }
#endif
            
            switch (req)
            {
                case IDLE:
                {
                    listen = true;
                    break;
                }
                
                case LOGIN:
                {
#ifdef DEBUG_OUTPUT
                    cout << "login request" << endl;
#endif
                    listen = false;
                    //listen = true;
                    clientRequest = LOGIN;
                    serverModeOutput = false;
                    break;
                }

                case UNLOGIN:
                {
#ifdef DEBUG_OUTPUT
                    cout << "unlogin request" << endl;
#endif
                    listen = false;
                    clientRequest = UNLOGIN;
                    serverModeOutput = false;
                    req = IDLE;
                    break;
                }
            
                case GET_USER_BY_ID:
                {
#ifdef DEBUG_OUTPUT
                    cout << "getting user by his ID" << endl;
#endif
                    listen = false;
                    serverModeOutput = false;
                    AddingRequest = true;
                    clientRequest = GET_USER_BY_ID;
                    
                    break;
                }
                
            }
        
        }
        else
        {
#ifdef DEBUG_OUTPUT
            if (!serverModeOutput)
            {
                threadMessage = "server is busy";
                cout << threadMessage.toAscii().data() << endl;
                
                serverModeOutput = true;
            }
#endif
            switch (clientRequest)
            {
                case IDLE:
                {
                    
                    break;
                }
                
                case LOGIN:
                {
#ifdef DEBUG_OUTPUT
                    cout << "logining to server" << endl;
#endif
                    cout << "query size: " << query.size() << endl;
                    addQueryInQueue(LOGIN, "");
                    
                    //req = UNLOGIN;
                    //req = IDLE;
                    //listen = true;
                    break;
                }
                
                case UNLOGIN:
                {
#ifdef DEBUG_OUTPUT
                    cout << "unlogining to server" << endl;
#endif                    
                    addQueryInQueue(UNLOGIN, "");
                    AddingRequest = false;
                    req = IDLE;
                    break;
                }
            
                case GET_USER_BY_ID:
                {
#ifdef DEBUG_OUTPUT
                    cout << "try get user by id" << endl;
#endif                    
                    addQueryInQueue(GET_USER_BY_ID, parametr);
                    req = IDLE;
                    clientRequest = IDLE;
                    break;
                }
                
            }

            
        }
    }
    while (query.size() > 0);
}

int thread_Engine::checkQueue()
{
    int returnCode = 0;
    /*
        0: очередь пуста
        1..: количество запросов в очереди
        ..-1: ошибки
     */
    
    returnCode = query.size(); 
    return returnCode;
}


void thread_Engine::QueueOperation()
{
    if (checkQueue() == 0)
    {
        listen = true;
    }
    else
    {
        while (checkQueue() > 0)
        {
        listen = false;
    
        if (inWork == false)
        {
#ifdef DEBUG_OUTPUT        
            cout << "current operation: " << currentOperation(query.front().operation) << endl;
#endif        
            sendData(query.front().req);
            query.erase(query.begin());
        }
        }
    }
}

char* thread_Engine::currentOperation(request req)
{
    switch (req)
    {
        case LOGIN:
        {
            return (char*)"LOGIN";
            break;
        }
        
        case UNLOGIN:
        {
            return (char*)"UNLOGIN";
            break;
        }
        
        case GET_USER_BY_ID:
        {
            return (char*)"GET_USER_BY_ID";
            break;
        }
        
        default:
        {
            return (char*)"UNDEFINED OPERATION";
            break;
        }
    }
}

int thread_Engine::addQueryInQueue(request newRequest, QString parametrs)
{
    switch (newRequest)
    {
        case LOGIN:
        {
            loginToBase();
            break;
        }
        case UNLOGIN:
        {
            unloginFromBase();
            
            break;
        }
        case GET_USER_BY_ID:
        {
            checkLoginUnloginInQueue(newRequest, parametrs);
            break;
        }
    }

    return 0;
}

int thread_Engine::connectToHostBase(QString host, int port)
{
    //cout << "try to connect" << endl;
    tcpSocket->connectToHost(host, port);
    if (!tcpSocket->waitForConnected(5000)) 
    {
#ifdef DEBUG_OUTPUT
        cout << "Connect Unsuccessful" << endl;
#endif        
        return 0;
    }
    else
    {
#ifdef DEBUG_OUTPUT
        cout << "Connect successful!" << endl;
#endif        
        return 1;
    }
}

void thread_Engine::loginToBase()
{
    if (connectToHostBase(HOST, PORT))
    {
        needReadAnswer = false;
        reqCounter = 1;
        queueRequest.id = query.size() + 1;
        queueRequest.req = loginRequest("A", "C");
        queueRequest.operation = LOGIN;
        query.push_back(queueRequest);
        connected = true;
    }
}

void thread_Engine::unloginFromBase()
{
    needReadAnswer = false;
    queueRequest.id = query.size() + 1;
    queueRequest.req = unLoginRequest("B", "C");
    queueRequest.operation = UNLOGIN;
    query.push_back(queueRequest);      
    connected = false;
}

QString thread_Engine::loginRequest(QString codeCommand, QString codeAPM)
{
    QString request = codeCommand + '\n' + codeAPM + '\n' + codeCommand + '\n' 
                        + QString::number(MAGIC_USER_ID) + '\n' + QString::number(reqCounter++) + '\n' 
                        + login + '\n' + pass + "\n\n\n\n"
                        + login + '\n' + pass;
    request.prepend(QString::number(request.length()) + '\n');
    return request;
}

QString thread_Engine::unLoginRequest(QString codeCommand, QString codeAPM)
{
    QString request = codeCommand + '\n' + codeAPM + '\n' + codeCommand + '\n' 
                        + QString::number(MAGIC_USER_ID) + '\n' + QString::number(reqCounter++) + '\n' 
                        + login + '\n' + pass + "\n\n\n\n"
                        + login;
    request.prepend(QString::number(request.length()) + '\n');
    return request;
}

int thread_Engine::sendData(QString data)
{
    lastBuffer.clear();
    inWork = true;
    connectToHostBase(HOST, PORT);
        
    if (tcpSocket->isOpen())
    {
        tcpSocket->write(data.toAscii().data());
        if (tcpSocket->waitForBytesWritten())
        {
#ifdef DEBUG_OUTPUT            
            cout << "send successful" << endl;
#endif
            return 1;
        }
        else
        {
#ifdef DEBUG_OUTPUT
            cout << "sending failed" << endl;
#endif
            return 0;
        }
    }
    else
        return 0;
}

/*
void thread_Engine::slotConnected()
{
#ifdef DEBUG_OUTPUT
    cout << "Connected!" << endl;
#endif
}

void thread_Engine::slotDisConnected()
{
#ifdef DEBUG_OUTPUT
    cout << "DisConnected!" << endl;
#endif
}

void thread_Engine::slotReadBytes()
{
#ifdef DEBUG_OUTPUT
    cout << "begin read answer" << endl;
#endif
    while (!tcpSocket->atEnd())
    //while (this->waitForReadyRead(1000))
    {
//        while (!this->atEnd())
//        {

        //cout << "bytes for read available: " << this->bytesAvailable() << endl;
        //QByteArray buf = this->readAll();
        QByteArray buf = tcpSocket->readAll();
        qint64 bytesReaded = buf.count();
        lastBuffer.append(QString::fromUtf8(buf.data()).toAscii().data());
        cout << "buffer readded = " << QString::number(bytesReaded).toAscii().data() << endl; 
    }

//    if (this->waitForReadyRead(300))
//    {
        inWork = false;
//    }
#ifdef DEBUG_OUTPUT
    cout << "end read answer" << endl;
#endif
}
*/

void thread_Engine::getUserByID(QString id)
{
    queueRequest.operation = GET_USER_BY_ID;
    queueRequest.id = query.size() + 1;
    queueRequest.req = generalRequestEditEnabled("K", "C", "RDR\n\"RI=" + id + "\"\n45\n1\nmpl,'&&&&&','10#',v10,'11#',v11,'12#',v12,'/',v40");
    queueRequest.needReadAnswer = true;
    query.push_back(queueRequest);
}

QString thread_Engine::generalRequestEditEnabled(QString codeCommand, QString codeAPM, QString parameters)
{
    QString request = codeCommand + '\n' + codeAPM + '\n' + codeCommand + '\n'
                        + QString::number(MAGIC_USER_ID) + '\n' + QString::number(reqCounter++) + '\n'
                        + login + '\n' + pass + '\n' + "\n\n\n" + parameters;
    return request;
}

void thread_Engine::loginToIrbis()
{
    req = LOGIN;
}

void thread_Engine::getUser_ID(QString id)
{
    parametr = id;
    req = GET_USER_BY_ID;
}

void thread_Engine::checkLoginUnloginInQueue(request req, QString param)
{
    bool hasLogin = false;
    bool hasUnlogin = false;
    
    cout << "check query for login & unlogin" << endl;
    
//    for (unsigned int i = 0; i < query.size(); i++) // перебор очереди
//    {
    if (checkQueue() > 0)
    {
        if (query.front().operation == LOGIN) // если присутствует логин
        {
            hasLogin = true;
        }
        if (query.back().operation == UNLOGIN) // если присутствует логин
        {
            hasUnlogin = true;
        }
//    }
    }
    
    if ((hasLogin == false) || (checkQueue() == 0)) // добавление в очередь логина
    {
        //clientRequest = LOGIN;
        loginToBase();
    }
    
    if (hasUnlogin)
    {
        query.erase(query.end());
    }
    
    switch (req)
    {
        case GET_USER_BY_ID:
        {
            getUserByID(param);
            unloginFromBase();
            AddingRequest = false;
            req = IDLE;
            //clientRequest = UNLOGIN;
            break;
        }
    }

    
    cout << "size query after checking: " << query.size() << endl;
}